package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.device.dao.mapper.StationMapper;
import cn.tedu.charging.device.dao.repository.StationRepository;
import cn.tedu.charging.device.pojo.po.StationPO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

/**
 * 数据仓库层
 * @Author: goodman
 * @Date: 2025/4/25 19:54
 */
@Repository
public class StationRepositoryImpl implements StationRepository {

    @Autowired
    private StationMapper stationMapper;

    @Autowired //注入对象,可以使用对象了
    private RedisTemplate redisTemplate;

    @Override
    public List<StationPO> selectAllStation() {
        QueryWrapper queryWrapper = new QueryWrapper();
        //查询列表 返回List
        //queryWrapper 用来添加条件的 可以添加多个条件
        //return stationMapper.selectList(queryWrapper);
        //null 表示查询所有 没有条件
        return stationMapper.selectList(null);
    }

    /**
     * 把所有场站信息保存到Redis中
     * 采用的是分开存储的方案 好处 基本信息可以复用
     * 位置信息 保存到 Redis的GEO类型
     * 基本信息 保存到 Redis的String类型
     *
     * @param stationPOS
     */
    @Override
    public void saveAllStation2Cache(List<StationPO> stationPOS) {
        String key = "stations_geo";
        String infoKey = "station_string";
        //opsForGeo:操作 GEO 类型
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        //opsForValue:操作String 类型
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //循环所有场站信息
        for (StationPO stationPO : stationPOS) {
            //通过每个场站信息 构建Point对象 点对象 经纬度表示一个点
            Point point = new Point(stationPO.getStationLng().doubleValue(),stationPO.getStationLat().doubleValue());
            //把每个场站的位置信息 add 存储到 RedisGEO 类型 key中
            // 方案2  位置和场站详情分开存储
            // 优点 场站信息可以复用
            // 缺点 数据存储了两份 查询的时候需要查两次 先通过位置查询 id 然后通过id 查询详情
            // 1 key stations  2 point 场站对应的位置 3 member id 场站id
            geoOperations.add(key,point,stationPO.getId());  //add方法GEO类型的存储方法
            // 把每个场站的详情信息  存储到 RedisString 类型 key station_id value 场站详情
            valueOperations.set(infoKey + stationPO.getId(),stationPO); //set方法String类型的存储方法

            // 方案1  位置 和 场站详情 存储到一起
            // 优点 简单 直接可以通过位置查询 场站
            // 缺点 场站详情信息不能被复用
            // 1 key stations  2 point 场站对应的位置 3 member 场站信息
            //geoOperations.add(key,point,stationPO);
        }
    }

    /**
     * 查找附近的场站
     * @param longitude 经度
     * @param latitude 纬度
     * @param radius 范围
     * @return
     */
    @Override
    public List<StationPO> nearStations(Double longitude, Double latitude, Double radius) {
        // 操作 GEO 类型  opsForGeo
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        //操作 String 类型  opsForValue
        ValueOperations<String, StationPO> valueOperations = redisTemplate.opsForValue();
        //通过入参 用户的位置 经度 和 纬度 创建一个点 构建Point对象 点对象 经纬度表示一个点
        Point point = new Point(longitude,latitude);
        //通过入参 用户要查找的范围 范围  创建一个 距离对象  单位距离 充电业务 选择的是千米 共享单车或者充电宝单位应该用米
        Distance distance = new Distance(radius, Metrics.KILOMETERS);
        //通过点对象 和 距离对象 创建一个圆对象
        Circle circle = new Circle(point,distance);

        //应该定义为常量
        String key = "stations_geo";
        String infoKey = "station_string_";

        //GeoLocation范型的类型为Integer 表示的member 场站ID的 类型  stationPO.getId()
        //Long add(K key, Point point, M member);
        // radius 方法 返回的是 附近的 场站id 列表
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> geoResults =
                geoOperations.radius(key, circle);
        //定义 返回结果  List<StationPO>
        List<StationPO> result = new ArrayList<>();
        //循环   返回的是 附近的 场站id 列表
        for (GeoResult<RedisGeoCommands.GeoLocation<Integer>> geoResult : geoResults.getContent()) {
            //name 表示的就是 member 场站id
            Integer name = geoResult.getContent().getName();
            //通过 场站id 获取 场站详情
            StationPO stationPO = valueOperations.get(infoKey + name);
            //把场站详情添加到返回结果中
            result.add(stationPO);
        }
        return result;
    }
}
